package org.jiangy.authentication.web;

import cn.hutool.crypto.digest.DigestUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import org.jiangy.Constants;
import org.jiangy.authentication.AuthenticationContext;
import org.jiangy.authentication.AuthenticationContextHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;
import java.util.UUID;

/**
 * @author Administrator
 */
@RequestMapping("/auth")
@RestController
public class AuthController {

    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;
    private final AuthenticationManager authenticationManager;
    private final UserDetailsManager userDetailsManager;
    private final HttpServletRequest request;

    public AuthController(RedisTemplate<String, String> redisTemplate, ObjectMapper objectMapper, AuthenticationManager authenticationManager, UserDetailsManager userDetailsManager, HttpServletRequest request) {
        this.redisTemplate = redisTemplate;
        this.objectMapper = objectMapper;
        this.authenticationManager = authenticationManager;
        this.userDetailsManager = userDetailsManager;
        this.request = request;
    }

    /**
     * 用户登录
     */
    @PostMapping("/login")
    public ResponseEntity<Object> login(@RequestBody LoginRequest loginRequest) throws JsonProcessingException {
        try {
            AuthenticationContextHolder.setContext(new AuthenticationContext(Map.of("credentialType", loginRequest.credentialType)));
            UsernamePasswordAuthenticationToken unauthenticated = UsernamePasswordAuthenticationToken
                    .unauthenticated(loginRequest.username(), loginRequest.password());
            unauthenticated.setDetails(Map.of("ipAddress", request.getRemoteAddr(),
                    "loginMethod", loginRequest.credentialType, "userAgent", request.getHeader("User-Agent")));

            Authentication authenticationResponse = this.authenticationManager.authenticate(unauthenticated);
            String userValue = objectMapper.writeValueAsString(authenticationResponse.getPrincipal());

            // 将用户信息存储到 redis
            String token = UUID.randomUUID().toString().replaceAll("-", "");
            String ts = DigestUtil.md5Hex16(token);
            redisTemplate.opsForValue().set(Constants.BJQ_USER_PROFILE.formatted(ts), userValue);

            return ResponseEntity.ok(ts);
        } finally {
            AuthenticationContextHolder.clearContext();
        }
    }

    @PostMapping("/reset-password")
    public ResponseEntity<?> forgetPassword(@RequestBody @Valid ForgetPasswordRequest request) {
        userDetailsManager.changePassword(request.oldPassword(), request.password());
        return ResponseEntity.noContent()
                .build();
    }

    /**
     * 声明一个不可变类型，JVM 将自动生成 getter、setter、 equals、hashCode 和 toString。
     * 要求：Java14+
     *
     * @param username       用户名
     * @param password       密码
     * @param credentialType 凭据类型 password sms scan
     */
    public record LoginRequest(String username, String password, String credentialType) {
    }

    public record ForgetPasswordRequest(@NotBlank String username, @NotBlank String oldPassword,
                                        @NotBlank String password) {

    }
}
